Emit experimental diagnostics in C# clients - #11685
Conversation
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: f0e7f0ac-c4b6-47e3-a4fc-430ff8f883c6
commit: |
|
No changes needing a change description found. |
| }; | ||
| } | ||
|
|
||
| function isEmitterScopeApplicable(emitterScope: string | undefined): boolean { |
There was a problem hiding this comment.
Isn't this already handled by TCGC?
There was a problem hiding this comment.
TCGC does filter scope, but it currently checks only a top-level decoratorInfo.arguments["scope"]. @experimental receives ClientDecoratorOptions through its options parameter, so this value arrives as decoratorInfo.arguments.options.emitterScope and is not filtered by TCGC. I kept the local check, added a comment explaining the distinction, and retained coverage verifying metadata scoped to another emitter is ignored.
--generated by Copilot
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: f0e7f0ac-c4b6-47e3-a4fc-430ff8f883c6
There was a problem hiding this comment.
Pull request overview
This PR propagates TypeSpec.HttpClient.@experimental metadata through the C# emitter input model and uses it during C# client generation to (a) emit ExperimentalAttribute on generated API methods and (b) add scoped warning suppressions for diagnostics listed in dependsOn.
Changes:
- Extend the emitter/input-model contract to carry
experimental: { diagnosticId, dependsOn }for operations, including JSON deserialization support and unit tests. - Emit
[Experimental("...")]on generated protocol and convenience methods whendiagnosticIdis present, and add#pragma warning disable/restorefor eachdependsOndiagnostic on generated methods (including request creation methods). - Add emitter-side decorator extraction + tests to ensure
@experimentalmetadata is captured with correct emitter scoping behavior.
Reviewed changes
Copilot reviewed 14 out of 14 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/common/InputFactory.cs | Adds optional experimental details when creating InputOperation test inputs. |
| packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/test/TypeSpecInputConverterTests.cs | Adds a deserialization test validating experimental.diagnosticId and experimental.dependsOn. |
| packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/InputTypes/Serialization/InputOperationConverter.cs | Deserializes the new experimental payload into InputOperation. |
| packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/InputTypes/InputOperation.cs | Stores Experimental details on InputOperation and threads it through constructors. |
| packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.Input/src/InputTypes/InputExperimentalDetails.cs | Introduces a new input-model type to represent experimental diagnostic metadata. |
| packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ScmMethodProviderCollectionTests.cs | Validates attribute emission and dependency suppressions for experimental operations. |
| packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Utilities/ExperimentalApiHelpers.cs | Centralizes building experimental attributes and dependency suppressions. |
| packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ScmMethodProviderCollection.cs | Applies experimental attributes/suppressions to generated protocol and convenience methods. |
| packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/RestClientProvider.cs | Applies dependency suppressions to generated create-request methods. |
| packages/http-client-csharp/emitter/test/Unit/experimental-decorator.test.ts | Adds unit tests for extracting @experimental diagnostic metadata (including emitter scoping). |
| packages/http-client-csharp/emitter/src/type/input-operation.ts | Extends the TS input operation type to include experimental. |
| packages/http-client-csharp/emitter/src/options.ts | Ensures @experimental is captured via additionalDecorators. |
| packages/http-client-csharp/emitter/src/lib/operation-converter.ts | Populates operation.experimental from decorators during conversion. |
| packages/http-client-csharp/emitter/src/lib/decorators.ts | Adds getExperimentalDetails and emitter-scope filtering for @experimental metadata. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| const scopes = emitterScope | ||
| .split(",") | ||
| .map((scope) => scope.trim()) | ||
| .filter((scope) => scope.length > 0); | ||
| const excludedScopes = scopes | ||
| .filter((scope) => scope.startsWith("!")) | ||
| .map((scope) => scope.slice(1)); | ||
| if (excludedScopes.length > 0) { | ||
| return !excludedScopes.includes(csharpEmitterName); | ||
| } | ||
|
|
||
| return scopes.includes(csharpEmitterName); |
There was a problem hiding this comment.
I'm wondering if tcgc already exports some util function we can use here instead ?
There was a problem hiding this comment.
Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.
Note
This error may be related to your runner configuration. You can now configure runners for Copilot code review separately from Copilot cloud agent by creating a copilot-code-review.yml file with your setup steps. Read the docs for details.
| crossLanguageDefinitionId: string; | ||
| decorators?: DecoratorInfo[]; | ||
| namespace?: string; | ||
| experimental?: InputExperimentalDetails; |
There was a problem hiding this comment.
are we intionally scoping this only for operations at the moment? I'm assuming we'll want to use this for other types in the future ?
|
|
||
| namespace Microsoft.TypeSpec.Generator.Input | ||
| { | ||
| public sealed class InputExperimentalDetails |
There was a problem hiding this comment.
do we need a serialization / converter type for this ?
Jorge Rangel (jorgerangel-msft)
left a comment
There was a problem hiding this comment.
do we need to update the tspd docs ?
## Motivation `@experimental` currently records only that a TypeSpec declaration is experimental. That is enough for emitters that need a boolean lifecycle flag, but it does not describe how an emitter should identify the experiment or whether the generated implementation relies on other experimental features. A generated API can be a distinct public experiment while being composed from several lower-level experiments. For example, an operation tracked as experiment `C` may internally use experimental features `A` and `B`. Consumers should see `C`, while an emitter may need `A` and `B` to generate warning suppressions around implementation code. ## Usage ```typespec @experimental(#{ emitterScope: "@typespec/http-client-csharp", diagnosticId: "C", dependsOn: #["A", "B"] }) op bar(): void; ``` ### Scenarios - **Direct experiment:** specify only `diagnosticId` when an emitter needs a stable identifier for the experimental API. - **Composed experiment:** use `dependsOn` when the generated implementation consumes other independently experimental features. - **Emitter-specific diagnostics:** use `emitterScope` when identifiers are meaningful only to a particular emitter or target language. - **Lifecycle tooling:** emitters and tooling can query the complete lifecycle details without parsing decorator syntax. - **Explicit graduation:** dependencies becoming generally available does not silently graduate the public API. The declaration remains experimental until its decorator is explicitly removed. ## API changes - Add optional `diagnosticId` and `dependsOn` fields to `FeatureLifecycleOptions`. - Add `getFeatureLifecycleDetails`, returning: ```ts { stage: "Experimental"; diagnosticId?: string; dependsOn: readonly string[]; } ``` - Preserve the existing `getFeatureLifecycle` API and its `"Experimental" | undefined` behavior for compatibility. - Apply existing emitter-scope filtering to all lifecycle details. ## Emitter behavior The metadata is descriptive rather than prescriptive. Each emitter decides how to represent it. The C# companion implementation in microsoft#11685 maps `diagnosticId` to `ExperimentalAttribute` and `dependsOn` to scoped warning suppressions. ## Related work - C# emitter implementation: microsoft#11685 - Design issue: microsoft#11690 ## Validation - `@typespec/http-client` build and full test suite - Affected-package lint and formatting checks Copilot-Session: f0e7f0ac-c4b6-47e3-a4fc-430ff8f883c6
Summary
TypeSpec.HttpClient.@experimentaldiagnostic metadata in the C# input model[Experimental("C")]on generated protocol and convenience methodsdependsOndiagnostic in generated method and request bodiesCompanion to #11684, which adds
diagnosticIdanddependsOnto the shared decorator contract.Validation